<template>
  <div class="pg-plum">
    <canvas ref="el" :width="WIDTH" :height="HEIGHT"></canvas>
  </div>
</template>

<script lang="ts" setup>
import { Point, Branch } from './types'

import { ref, onMounted, computed } from 'vue'

const WIDTH = 300
const HEIGHT = 300

const el = ref<HTMLCanvasElement>()
const ctx = computed(() => el.value!.getContext('2d')!)
let pendingTasks: any[] = []
function init() {
  ctx.value.fillStyle = '#fff'
  step({
    start: { x: WIDTH / 2, y: HEIGHT },
    length: 20,
    theta: -Math.PI / 2,
  })
}
function step(branch: Branch, depth = 0) {
  const end = getEndPoint(branch)
  drawBranch(branch, end)
  if (depth < 6) {
    pendingTasks.push(() =>
      step(
        {
          start: end,
          length: branch.length + (Math.random() * 10 - 5),
          theta: branch.theta - 0.3 * Math.random(),
        },
        depth + 1
      )
    )
    pendingTasks.push(() =>
      step(
        {
          start: end,
          length: branch.length + (Math.random() * 10 - 5),
          theta: branch.theta + 0.3 * Math.random(),
        },
        depth + 1
      )
    )
  }
}

function frame() {
  const curTask = [...pendingTasks]
  pendingTasks = []
  curTask.forEach((fn) => fn())
}
let frameCount = 0
function startFrame() {
  requestAnimationFrame(() => {
    frameCount += 1
    if (frameCount % 3 === 0) frame()
    startFrame()
  })
}
startFrame()

function drawBranch(branch: Branch, end: Point) {
  lineTo(branch.start, end)
}
function lineTo(p1: Point, p2: Point) {
  ctx.value.beginPath()
  ctx.value.moveTo(p1.x, p1.y)
  ctx.value.lineTo(p2.x, p2.y)
  ctx.value.stroke()
  ctx.value.closePath()
}

function getEndPoint(startPoint: Branch): Point {
  return {
    x: startPoint.start.x + startPoint.length * Math.cos(startPoint.theta),
    y: startPoint.start.y + startPoint.length * Math.sin(startPoint.theta),
  }
}

onMounted(() => {
  init()
})
</script>

<style lang="scss" scoped>
canvas {
  margin: 20px auto;
  border: 1px solid red;
}
</style>
